/**
* @brief
*		Find errors and decrease probability of getting errors of the same kind in the future
*		This piece of code won't compile and it doesn't describe an entire algorithm: just part of some page storage
*
* @author
*		AnnaM
*/

//#include <Windows.h>
#include <stdio.h>

enum PAGE_COLOR {		// FIXED(1): incorrect color numeration(from 1) - "incorrect" means - incorrect with future usage
	PG_COLOR_GREEN,		/* page may be released without high overhead */
	PG_COLOR_YELLOW,	/* nice to have */
	PG_COLOR_RED			/* page is actively used */
};

#define PG_COLOR_NUMBER 3

/**
 * UINT Key of a page in hash-table (prepared from color and address)
 */
union PageKey {
	struct {
    CHAR cColor : 8;
		UINT cAddr  : 24;
	};

	UINT uKey;
};

/* Prepare from 2 chars the key of the same configuration as in PageKey */
#define CALC_PAGE_KEY(Addr, Color) ((Color) + ((Addr) << 8))
// FIXED(2): to estimate the correct key the order of the operations was changed

/**
 * Descriptor of a single guest physical page
 */
struct PageDesc {
	PageKey pKey;

	/* list support */
	PageDesc *next, *prev;
};

#define PAGE_INIT(Desc, Addr, Color)												\
	/* FIXED(3): do {...} while(0) */													\
	do {																											\
		/* FIXED(4): Desc should not be called several times */	\
		PageDesc *descr = Desc;																	\
		descr->pKey.uKey = CALC_PAGE_KEY(Addr, Color);					\
		descr->next = descr->prev = NULL;												\
  } while (0)


/* storage for pages of all colors */
static PageDesc* PageStrg[PG_COLOR_NUMBER]; // массив указателей - типа хеша что-то

void PageStrgInit() {
	/* FIXED(5): Correct space size was set */
	memset(PageStrg, 0, PG_COLOR_NUMBER * sizeof(&PageStrg));
}

PageDesc* PageFind(void *addr, PAGE_COLOR color) {
	for (PageDesc *Pg = PageStrg[color]; Pg; Pg = Pg->next)
		// FIXED(6): Correct struct field was set
		if (Pg->pKey.uKey == CALC_PAGE_KEY(*(UINT*)addr, color))
			return Pg;

	return NULL;
}

PageDesc* PageReclaim(UINT cnt) {
	PAGE_COLOR color = PG_COLOR_GREEN;
	// FIXED(7): Uninitialized ptr was removed
	PageDesc *Pg = PageStrg[color];
	while ((cnt) && (color < PG_COLOR_NUMBER)) {
		if (Pg) {
			// FIXED(15): we should change PgStrg[color] ptr when we remove a page
			PageStrg[color] = Pg;
			Pg = Pg->next;
			PageRemove(PageStrg[color]);
			--cnt;
		} else {
			Pg = PageStrg[++color];
		}
	}
	// FIXED(8): Non-void function now returns value
	if (cnt)
		return NULL;
	return PageStrg[color];
}

PageDesc* PageInit(void *addr, PAGE_COLOR color) {
  PageDesc *pg;
	try {
		// FIXED(9): new operator exception is handled
		pg = new PageDesc;
	} catch (std::bad_alloc &e) {
		return NULL;
	}
	// FIXED(10): pg - is a valid PageDescr ptr now
	PAGE_INIT(pg, *(UINT*)addr, color);
	return pg;
}

/**
 * Print all mapped pages
 */
void PageDump() {
	PAGE_COLOR color = PG_COLOR_GREEN;
#define PG_COLOR_NAME(clr) #clr
	// FIXED(11): Correct color order was set
	char *PgColorName[] = {
		PG_COLOR_NAME(PG_COLOR_GREEN),
		PG_COLOR_NAME(PG_COLOR_YELLOW),
		PG_COLOR_NAME(PG_COLOR_RED),
	};

	while (color <= PG_COLOR_RED) {
		// FIXED(12): %u and %s now get correct type values
		printf("PgStrg[(%u) %s] ********** \n", color, PgColorName[color]);
		// FIXED(16): not ++color, but color++
		for (PageDesc *Pg = PageStrg[color++]; Pg; Pg = Pg->next) {
			// FIXED(13): = -> ==
			// FIXED(14): Pg doesn't have a field uAddr
			if (Pg->pKey.cAddr == 0x0)
				continue;
			printf("Pg :Key = 0x%x, addr %d\n", Pg->pKey.uKey, Pg->pKey.cAddr);
		}
	}
#undef PG_COLOR_NAME
}
